package com.partsoft.dits.dsm;

import javax.sql.DataSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import com.partsoft.dits.ConfigurableBean;

/**
 * 抽象的数据源实例获取类
 * @author neeker
 */
public abstract class AbstractDataSourceResolveBean implements ApplicationContextAware,
		ApplicationListener<ApplicationEvent>, InitializingBean, ConfigurableBean {

	protected ApplicationContext ownerContext;

	protected DataSourceFactory dataSourceFactory;

	protected DataSource dataSource;

	protected String dataSourceName;

	protected DataSource resolvedDataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	protected boolean isResolveInit() {
		return resolvedDataSource != null;
	}

	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.ownerContext = context;
	}

	public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
		this.dataSourceFactory = dataSourceFactory;
	}

	public DataSourceFactory getDataSourceFactory() {
		return dataSourceFactory;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public void setOwnerContext(ApplicationContext ownerContext) {
		this.ownerContext = ownerContext;
	}

	public ApplicationContext getOwnerContext() {
		return ownerContext;
	}

	public void initContext() {
		synchronized (this) {
			if (isResolveInit())
				finiContext();
			String dsName = getDataSourceName();
			if (this.dataSource != null) {
				this.resolvedDataSource = this.dataSource;
			} else {
				Assert.isTrue(getDataSourceFactory().hasDataSource(dsName), String.format("Cann't found datasource(%s)", dsName));
				this.resolvedDataSource = getDataSourceFactory().getDataSource(dsName);
			}
		}
	}

	protected void assertInited() {
		Assert.isTrue(isResolveInit(), "datasource not initialize");
	}

	protected void assertNotInited() {
		Assert.isTrue(!isResolveInit(), "datasource already initialized");
	}

	/**
	 * 表示已获取到的数据源对象
	 * @return
	 */
	protected DataSource getResolvedDataSource() {
		assertInited();
		return resolvedDataSource;
	}

	public void finiContext() {
		synchronized (this) {
			this.resolvedDataSource = null;
		}
	}

	public void afterPropertiesSet() throws Exception {
		if (dataSource == null) {
			Assert.notNull(getDataSourceFactory(), "Must be set datasource factory");
			Assert.hasText(getDataSourceName(), "Must be set datasource name");
		}
	}

	public void onApplicationEvent(ApplicationEvent event) {
		if (ObjectUtils.nullSafeEquals(event.getSource(), getOwnerContext())) {
			if (event instanceof ContextRefreshedEvent) {
				initContext();
			} else if (event instanceof ContextClosedEvent) {
				finiContext();
			}
		}
	}

}
